package com.example.mycomposelearing.gesture.ui

import android.util.Log
import androidx.compose.animation.core.*
import androidx.compose.animation.splineBasedDecay
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.awaitTouchSlopOrCancellation
import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.gestures.horizontalDrag
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlin.math.roundToInt

@Composable
fun DragPlayGround() {
    var horizontalVelocity by remember {
        mutableStateOf<Float>(0f)
    }
    var verticalVelocity by remember {
        mutableStateOf<Float>(0f)
    }
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = "Velocity",
                    fontSize = 42.sp,
                    fontWeight = FontWeight.Bold
                )
            }
            Spacer(modifier = Modifier.height(10.dp))
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = String.format("Horizontal: %.2f Vertical: %.2f", horizontalVelocity, verticalVelocity),
                    fontSize = 20.sp,
                    fontWeight = FontWeight.Bold
                )
            }
            Spacer(modifier = Modifier.height(20.dp))
            var offset = remember {
                Animatable(Offset.Zero, Offset.VectorConverter)
            }
            Box(
                Modifier
                    .size(350.dp)
                    .background(Color.Gray)
                    .pointerInput(Unit) {
                        offset.updateBounds(
                            lowerBound = Offset.Zero,
                            upperBound = Offset(320.dp.toPx(), 320.dp.toPx())
                        )
                        coroutineScope {
                            forEachGesture {
                                val down = awaitPointerEventScope { awaitFirstDown() }
                                offset.stop()
                                awaitPointerEventScope {
                                    var validDrag: PointerInputChange?
                                    do {
                                        validDrag =
                                            awaitTouchSlopOrCancellation(down.id) { change, _ ->
                                                change.consumePositionChange()
                                            }
                                        Log.d("TAG", "DragPlayGround1: $validDrag")
                                    } while (validDrag != null && !validDrag.positionChangeConsumed())
                                    if (validDrag != null) {
                                        Log.d("TAG", "DragPlayGround2: $validDrag")

                                        val velocityTracker = VelocityTracker()
                                        var dragAnimJob: Job? = null
                                        drag(validDrag.id) {
                                            dragAnimJob = launch {
                                                offset.snapTo(
                                                    offset.value + it.positionChange()
                                                )
                                                Log.d("TAG", "DraggPlayGround it.positionChange():${it.positionChange().y} ")
                                                Log.d("TAG", "DraggPlayGround  it.uptimeMillis:${ it.uptimeMillis} ")
                                                velocityTracker.addPosition(
                                                    it.uptimeMillis,
                                                    it.position
                                                )
                                                horizontalVelocity =
                                                    velocityTracker.calculateVelocity().x
                                                verticalVelocity =
                                                    velocityTracker.calculateVelocity().y
                                            }
                                        }
                                        horizontalVelocity = velocityTracker.calculateVelocity().x
                                        verticalVelocity = velocityTracker.calculateVelocity().y
                                        val decay = splineBasedDecay<Offset>(this)
                                        var targetOffset = decay
                                            .calculateTargetValue(
                                                Offset.VectorConverter,
                                                offset.value,
                                                Offset(horizontalVelocity, verticalVelocity)
                                            )
                                            .run {//相当于限制位置，超出会回到边界
                                                Offset(
                                                    x.coerceIn(0f, 320.dp.toPx()),
                                                    y.coerceIn(0f, 320.dp.toPx())
                                                )
                                            }
                                        dragAnimJob?.cancel()
                                        launch {
                                            offset.animateTo(
                                                targetOffset,
                                                tween(2000, easing = LinearOutSlowInEasing)
                                            )
                                        }
                                    }
                                }
                            }
                        }
                    }
            ) {
                Box(modifier = Modifier
                    .offset {
                        IntOffset(
                            x = offset.value.x.roundToInt(),
                            y = offset.value.y.roundToInt()
                        )
                    }
                    .size(30.dp)
                    .clip(CircleShape)
                    .background(Color.Green)
                )
            }
        }
    }
}

@Composable
fun DragToDismiss() {
    var offset = remember {
        Animatable(Offset(100f,100f), Offset.VectorConverter)
    }

    Box(modifier = Modifier
        .offset { IntOffset(x = offset.value.x.roundToInt(), y = offset.value.y.roundToInt()) }
        .size(50.dp)
        .clip(CircleShape)
        .background(Color.Green)
        .pointerInput(Unit) {
            val decay = splineBasedDecay<Offset>(this)
            // Use suspend functions for touch events and the Animatable.

            coroutineScope {
                while (true) {
                    val velocityTracker = VelocityTracker()
                    // Stop any ongoing animation.
                    offset.stop()
                    awaitPointerEventScope {
                        // Detect a touch down event.
                        val pointerId = awaitFirstDown().id

                        horizontalDrag(pointerId) { change ->
                            // Update the animation value with touch events.
                            launch {
                                offset.snapTo(
                                    offset.value + Offset(
                                        change.positionChange().x,
                                        change.positionChange().y
                                    )
                                )
                            }
                            velocityTracker.addPosition(
                                change.uptimeMillis,
                                change.position
                            )
                        }
                    }
                    // No longer receiving touch events. Prepare the animation.
                    val velocity = Offset(
                        velocityTracker.calculateVelocity().x,
                        velocityTracker.calculateVelocity().y
                    )
                    val targetOffset = decay.calculateTargetValue(
                        typeConverter = TwoWayConverter(
                            convertToVector = { offset: Offset ->
                                AnimationVector2D(offset.x, offset.y)
                            },
                            convertFromVector = { vector: AnimationVector2D ->
                                Offset(vector.v1, vector.v2)
                            }
                        ),
                        offset.value,
                        velocity
                    )
                    // The animation stops when it reaches the bounds.
//                    offset.updateBounds(
//                        lowerBound = Offset(0f, 0f),
//                        upperBound = Offset(500f, 500f),
//                    )
                    launch {
                        if (targetOffset.x <= 500f) {
                            // Not enough velocity; Slide back.
                            offset.animateTo(
                                targetValue = Offset(500f, 500f),
                                initialVelocity = velocity
                            )
                        } else {
                            // The element was swiped away.
                            offset.animateDecay(velocity, decay)
//                            onDismissed()
                        }
                    }
                }
            }
        }
    )
}